package com.dxt.netty;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dxt.common.AppConstant;
import com.dxt.common.HttpUtil;
import com.dxt.common.SpringUtil;
import com.dxt.dao.BusiLogDao;
import com.dxt.model.BusiLogBean;
import com.dxt.netty.utils.ReqStrUtils;
import com.dxt.service.CacheManager;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class RuiKongYdServerHandler extends ChannelInboundHandlerAdapter {

    private static final Logger logger = LoggerFactory.getLogger(Thread.currentThread().getStackTrace()[1].getClassName());
    private static Map<Object, Channel> onlineMap = new ConcurrentHashMap<>();
    //可以把loss_connect_time 放到AttributeMap中
    private int loss_connect_time = 0;
    private static CacheManager cacheManager;

    private static BusiLogDao busiLogDao;
    static {
        cacheManager = SpringUtil.getBean(CacheManager.class);
        busiLogDao = SpringUtil.getBean(BusiLogDao.class);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println(ctx.channel().remoteAddress()+"->Server read:"+msg.toString());
        String requstMsg = msg.toString();
        if("ARE YOU OK".equals(requstMsg)){
            // 返回客户端消息
            System.out.println("握手成功！"+requstMsg);
            ctx.write("FINE");
            ctx.flush();
        }else{
            //握手后正常处理业务
            //校验CRC16校验码是否正确
            if(ReqStrUtils.getSgin(requstMsg).equals(ReqStrUtils.signStr(requstMsg))){
                Map<String,Object> resMap = getReqJson(requstMsg);
                String reqJson = resMap.get("resJson").toString();
                String id = resMap.get("id").toString();
                System.out.println("reqJson="+reqJson);
                JSONObject json = JSONObject.parseObject(reqJson);
                Long deviceId = json.getLong("deviceId");
                if (!onlineMap.containsKey(deviceId)) {
                    onlineMap.put(deviceId,ctx.channel());
                    logger.info("onlineMap添加deviceId="+deviceId);
                    logger.info("onlineMap.size:"+onlineMap.size());
                }
                if(onlineMap.containsKey(deviceId) && onlineMap.get(deviceId) != ctx.channel()){
                    onlineMap.remove(deviceId);
                    onlineMap.put(deviceId,ctx.channel());
                }
                //调用消防系统接口，发送设备上报信息到消防系统
                String response = null;
                try {
                    response = HttpUtil.getHttpPostResponse(cacheManager.getSysConfigByCode(AppConstant.SYS_CONFIG_KEY.KEY_XF_POST_URL),
                            "json", reqJson,
                            cacheManager.getSysConfigByCode(AppConstant.SYS_CONFIG_KEY.KEY_TOKEN));
                } catch (IOException e) {
                    e.printStackTrace();
                }
                logger.info("response="+response);
                JSONObject jsonObj = null;

                if(StringUtils.isNotBlank(response)){
                    jsonObj = JSON.parseObject(response);
                }
                //发送成功后，更新日志状态，为已发送
                if(!jsonObj.isEmpty()){
                    try {
                        String code = jsonObj.getString(AppConstant.REQUEST_REPONSE_PARAM.PARAM_OUT_CODE);
                        if(StringUtils.isNotBlank(code) && AppConstant.REPONSE_CODE.SUCCESS_CODE.equals(code)){
                            BusiLogBean busiLogBean = new BusiLogBean();
                            busiLogBean.setId(BigDecimal.valueOf(Long.valueOf(id)));
                            busiLogBean.setDeviceId(String.valueOf(deviceId));
                            busiLogBean.setDealType(1);
                            busiLogBean.setDealDate(new Timestamp(System.currentTimeMillis()));
                            busiLogDao.modifyBusiLog(busiLogBean);
                        }else{
                            BusiLogBean busiLogBean = new BusiLogBean();
                            busiLogBean.setDeviceId(String.valueOf(deviceId));
                            busiLogBean.setId(BigDecimal.valueOf(Long.valueOf(id)));
                            busiLogBean.setDealType(2);
                            busiLogBean.setDealDate(new Timestamp(System.currentTimeMillis()));
                            busiLogDao.modifyBusiLog(busiLogBean);
                        }
                    }catch (Exception e) {
                        e.printStackTrace();
                    }
                }else{
                    logger.error("[%s],未得到正确相应",reqJson);
                }

                //发送失败，更新日志状态为失败，重新发送

                //重复完成后，更新日志状态，为已发送

            }else{
                logger.error("异常数据，记录不转发给消防系统");
                logger.error(requstMsg);
                //记录错误日志数据
            }
        }

    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }

    /**
     * 一旦有socket建立则发送读取设备ID的命令
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        logger.info(String.format("socket连接地址: %s",ctx.channel().remoteAddress()));
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
        Channel channel = ctx.channel();
        if(channel!=null) {
            if (onlineMap.containsValue(ctx.channel())) {
                Long deviceId = getKey(onlineMap,ctx.channel());
                onlineMap.remove(deviceId);
                //发送给消防系统
                String str = String.format("[%s]: 被动断开设备ID: %d", ctx.channel().remoteAddress(), deviceId);
                if(onlineMap.get(deviceId) == null ){
                    Map<String,Object> map = new HashMap<>();
                    map.put("deviceId",deviceId);
                    map.put("answerType","N");
                    map.put("orderType","6");
                    JSONObject reqJson = new JSONObject(map);
                    BusiLogBean busiLogBean = new BusiLogBean();
                    busiLogBean.setDeviceId(String.valueOf(deviceId));
                    busiLogBean.setOriginalStr(reqJson.toJSONString());
                    busiLogBean.setNewJson(reqJson.toJSONString());
                    busiLogBean.setLogType(1);
                    busiLogBean.setCreateDate(new Timestamp(System.currentTimeMillis()));
                    busiLogDao.addBusiLog(busiLogBean);
                    BigDecimal id = busiLogBean.getId();

                    //调用消防系统接口，发送设备上报信息到消防系统
                    String response = null;
                    try {
                        response = HttpUtil.getHttpPostResponse(cacheManager.getSysConfigByCode(AppConstant.SYS_CONFIG_KEY.KEY_XF_POST_URL),
                                "json", reqJson.toJSONString(),
                                cacheManager.getSysConfigByCode(AppConstant.SYS_CONFIG_KEY.KEY_TOKEN));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    JSONObject jsonObj = null;
                    if(StringUtils.isNotBlank(response)){
                        jsonObj = JSON.parseObject(response);
                    }
                    //发送成功后，更新日志状态，为已发送
                    try {
                        String code = jsonObj.getString(AppConstant.REQUEST_REPONSE_PARAM.PARAM_OUT_CODE);
                        if(StringUtils.isNotBlank(code) && AppConstant.REPONSE_CODE.SUCCESS_CODE.equals(code)){
                            BusiLogBean busiLogBean1 = new BusiLogBean();
                            busiLogBean1.setDeviceId(String.valueOf(deviceId));
                            busiLogBean1.setId(id);
                            busiLogBean1.setDealType(1);
                            busiLogBean.setDealDate(new Timestamp(System.currentTimeMillis()));
                            busiLogDao.modifyBusiLog(busiLogBean1);
                        }else{
                            BusiLogBean busiLogBean2 = new BusiLogBean();
                            busiLogBean2.setDeviceId(String.valueOf(deviceId));
                            busiLogBean2.setId(id);
                            busiLogBean2.setDealType(2);
                            busiLogBean.setDealDate(new Timestamp(System.currentTimeMillis()));
                            busiLogDao.modifyBusiLog(busiLogBean2);
                        }
                    }catch (Exception e) {
                        e.printStackTrace();
                    }

                }else{
                    logger.info(String.format("设备ID: %s,已重新握手成功",deviceId));
                }
                logger.info(str);
            }
        }
        ctx.channel().close();
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        Channel channel = ctx.channel();
        Long deviceId = getKey(onlineMap,channel);
        if(deviceId == 0 ){
            return;
        }
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            IdleState state=event.state();
            logger.info("state="+state);
            if (state==IdleState.READER_IDLE) {
                //设备离线，更改设备状态，增加离线操作日志
                loss_connect_time++;
                logger.info(deviceId+"客户端离线"+loss_connect_time+"周期了");
                if(loss_connect_time>=AppConstant.SYS_CONSTANT.MAX_LOSS_CONNECT_TIME){
                    //客户端断连3分钟
                    logger.info("服务器主动关闭客户端链路超过--"+AppConstant.SYS_CONSTANT.SERVER_READ_IDEL_TIME_OUT*loss_connect_time+"s的设备ID:"+ deviceId);
                    //发送离线短信通知客户
                    if (onlineMap.containsValue(ctx.channel())) {
                        onlineMap.remove(deviceId);
                        //发送给消防系统
                        String str = String.format("[%s]: 主动断开设备ID: %d", ctx.channel().remoteAddress(), deviceId);
                        if(onlineMap.get(deviceId) == null ){
                            Map<String,Object> map = new HashMap<>();
                            map.put("deviceId",deviceId);
                            map.put("answerType","N");
                            map.put("orderType","6");
                            JSONObject reqJson = new JSONObject(map);
                            BusiLogBean busiLogBean = new BusiLogBean();
                            busiLogBean.setDeviceId(String.valueOf(deviceId));
                            busiLogBean.setOriginalStr(reqJson.toJSONString());
                            busiLogBean.setNewJson(reqJson.toJSONString());
                            busiLogBean.setLogType(1);
                            busiLogBean.setCreateDate(new Timestamp(System.currentTimeMillis()));
                            busiLogDao.addBusiLog(busiLogBean);
                            BigDecimal id = busiLogBean.getId();

                            //调用消防系统接口，发送设备上报信息到消防系统
                            String response = null;
                            try {
                                response = HttpUtil.getHttpPostResponse(cacheManager.getSysConfigByCode(AppConstant.SYS_CONFIG_KEY.KEY_XF_POST_URL),
                                        "json", reqJson.toJSONString(),
                                        cacheManager.getSysConfigByCode(AppConstant.SYS_CONFIG_KEY.KEY_TOKEN));
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                            JSONObject jsonObj = null;
                            if(StringUtils.isNotBlank(response)){
                                jsonObj = JSON.parseObject(response);
                            }
                            //发送成功后，更新日志状态，为已发送
                            try {
                                String code = jsonObj.getString(AppConstant.REQUEST_REPONSE_PARAM.PARAM_OUT_CODE);
                                if(StringUtils.isNotBlank(code) && AppConstant.REPONSE_CODE.SUCCESS_CODE.equals(code)){
                                    BusiLogBean busiLogBean1 = new BusiLogBean();
                                    busiLogBean1.setDeviceId(String.valueOf(deviceId));
                                    busiLogBean1.setId(id);
                                    busiLogBean1.setDealType(1);
                                    busiLogBean.setDealDate(new Timestamp(System.currentTimeMillis()));
                                    busiLogDao.modifyBusiLog(busiLogBean1);
                                }else{
                                    BusiLogBean busiLogBean2 = new BusiLogBean();
                                    busiLogBean2.setDeviceId(String.valueOf(deviceId));
                                    busiLogBean2.setId(id);
                                    busiLogBean2.setDealType(2);
                                    busiLogBean.setDealDate(new Timestamp(System.currentTimeMillis()));
                                    busiLogDao.modifyBusiLog(busiLogBean2);
                                }
                            }catch (Exception e) {
                                e.printStackTrace();
                            }

                        }else{
                            logger.info(String.format("设备ID: %s,已重新握手成功",deviceId));
                        }
                        logger.info(str);
                    }
                    loss_connect_time=0;
                    //服务端主动关闭channel,会触发com.vendor.netty.server.NettyServerHandler.channelInactive()方法
                    ctx.channel().close();
                }
            } else {
                //复位
                logger.info(deviceId+"客户端恢复连接-----");
                loss_connect_time =0;
                super.userEventTriggered(ctx,evt);
            }
        } else {
            //复位
            logger.info(deviceId+"客户端恢复连接=======");
            loss_connect_time =0;
            super.userEventTriggered(ctx,evt);
        }
    }

    public static Channel getChannel(Long deviceId) {
        if (null == onlineMap) {
            onlineMap = new ConcurrentHashMap<>();
            logger.debug("onlineMap为空");
        }else{
            logger.debug(String.format("onlineMap.size: %s ",onlineMap.size()));
        }
        Channel channel = onlineMap.get(deviceId);
        return channel;
    }

    static Long getKey(Map map, Channel value) {
        Long deviceId = 0L;
        for(Object key: map.keySet()) {
            if(map.get(key).equals(value)) {
                deviceId = (Long) key;
                break;
            }
        }
        return deviceId;
    }

    /**
     * 根据设备上报的数据组成发送给消防系统的json报文
     * @param reqStr
     * @return
     */
    public Map<String,Object> getReqJson(String reqStr){
        Map<String,Object> resMap = new HashMap<>();
        Map<String,Object> map = new HashMap<>();
        String mapKey,mapValue,lastChar = null;
        //上报经纬度时，13，14位 是G: 单独判断
        String flag = reqStr.substring(13,15);
        String[] dataArrays = reqStr.substring(1,reqStr.length()-1).split(",");
        Long deviceId = Long.parseLong(dataArrays[0].substring(3,dataArrays[0].length()-0),16);
        String answerType = dataArrays[0].substring(1,2);
        String orderType = dataArrays[0].substring(2,3);
        map.put("deviceId",deviceId);
        map.put("answerType",answerType);
        map.put("orderType",orderType);
        if(!"G:".equals(flag)){
            for(int i = 1; i < dataArrays.length-1; i++){
                mapKey = dataArrays[i].substring(0,2);
                mapValue = Integer.valueOf(dataArrays[i].substring(2,dataArrays[i].length()-1),16).toString();
                lastChar = dataArrays[i].substring(dataArrays[i].length()-1,dataArrays[i].length()-0);
                if("1".equals(lastChar)){
                    mapValue = mapValue.substring(0,mapValue.length()-1) + "." + mapValue.substring(mapValue.length()-1,mapValue.length()-0);
                }
                if(".".equals(mapValue.substring(0,1))){
                    mapValue = "0"+mapValue;
                }
                map.put(mapKey,mapValue);
            }
        }else{
            String longitude = dataArrays[1].substring(2,dataArrays[1].length()-0);
            String latitude = dataArrays[2];
            map.put("longitude",longitude);
            map.put("latitude",latitude);
        }
        JSONObject json = new JSONObject(map);
        if(StringUtils.isNotEmpty(json.toJSONString())){
            BusiLogBean busiLogBean = new BusiLogBean();
            busiLogBean.setDeviceId(String.valueOf(deviceId));
            busiLogBean.setOriginalStr(reqStr);
            busiLogBean.setLogType(1);
            busiLogBean.setNewJson(json.toJSONString());
            busiLogBean.setCreateDate(new Timestamp(System.currentTimeMillis()));
            busiLogDao.addBusiLog(busiLogBean);
            resMap.put("id",busiLogBean.getId());
        }
        resMap.put("resJson",json);
        return resMap;
    }
}

